package com.bizmerce.ebms.engine;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Vector;

import javax.xml.namespace.QName;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.NodeList;

import com.bizmerce.ebms.Generator.MessageGenerator;
import com.bizmerce.ebms.cpa.Cpa;
import com.bizmerce.ebms.cpa.CpaProvider;

public class MessageProcessorImpl implements MessageProcessor {
	
	private CpaProvider cpaProvider;
	
	public void setCpaProvider(CpaProvider cpaProvider) {
		this.cpaProvider = cpaProvider;
	}

	/**
	 * TODO 1. MIMEHEADER에 따른 메시지 처리
	 * TODO 1.1 SOAP 메시지 처리
	 * TODO 2. XML 문서처리
	 * TODO 3. 첨부파일 처리 
	 * TODO 4. 응답메시지 리턴
	 * @param soapRequest
	 * @param soapResponse
	 * @throws FileNotFoundException 
	 */
	public SOAPMessage processReceiveMessage(MessageProcessorVo vo) {
		DataRepository repository = new DataRepository();
		//SOAPMessage 정보에 대한 검증
		if(true) {
			repository.registerMessage(vo);
			
			Payload[] payloads = vo.payloads();
			for (Payload payload : payloads) {
				repository.save(payload);
			}
		}
		return null;
	}
	
	/**
	 * TODO 1. 주어진 Cpa-Id를 기본으로 CpaProvider의 정보를 가지고 Soap메시지 구성
	 * TODO 2. XML 문서 담기
	 * TODO 3. 첨부파일 담기
	 * 아직 딱히 좋은 아이디어가 떠오르지 않는다.
	 * @param soapRequest
	 * @param soapResponse
	 */
	public void processSendMessage(String cpaId, Payload[] payloads) {
	//EbmsInProcessor, EbmsOutProcessor
		Cpa cpa = cpaProvider.getCpa(cpaId);
		try {
			SOAPMessage soapMessage = createSoapMessage(cpa, payloads);
		} catch (SOAPException e) {
			e.printStackTrace();
		}
	}
	
	public void processSendAckMessage(String cpaId) {
		
	}
	
	public void processReceiveAckMessage(MessageProcessorVo vo) {
		
	}
	

	/**
	 * Logger
	 */
	private final static Log log = LogFactory.getLog(MessageProcessor.class);
	
	
	// PKG 파일 경로
	private static String PKG_PATH = "C:/Documents and Settings/Microsoft/바탕 화면/soap/test/";
	
	// eb namespace url
	private static String NAMESPACE_EB = "http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd";
	
	// eb
	private static String EB = "eb";
	
	// xsi schemalocation url
	private static String XSI_SCHEMALOCATION = "http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd";
	
	// request url
	private static String REQUEST_URL = "http://localhost:8080/";
	
	// response url
	private static String RESPONSE_URL = "http://localhost:8080/";
	
	// PKG 파일명
	private static String PKG_FILE;
	
	
	public MessageProcessorVo messageProcessorVo;

	/**
	 *  객체 초기화
	 */
	public MessageProcessorImpl() {
		
		messageProcessorVo = new MessageProcessorVo();
	}

	public MessageProcessorImpl(String pkgFileName) {
		
		messageProcessorVo = new MessageProcessorVo();
		PKG_FILE           = pkgFileName;

	}
	
	public MessageProcessorVo getMessageProcessor() {
		
		return messageProcessorVo;
	}
	
	/**
	 * Soap Message 수신
	 */
	public void soapInBoundProcess() {
		
		MessageGenerator mg 	  = new MessageGenerator();
		String soapFileName       = "";     // SOAP Message 명
		String xmlFileName        = "";     // XML 파일명
		
		try {
			
			// PKG 파일을 
			Vector fileVec = mg.doPkgParse(PKG_PATH + PKG_FILE);
			MessageFactory mf       = MessageFactory.newInstance();
			
			int p = PKG_FILE.lastIndexOf(".");
			soapFileName = PKG_FILE.substring(0, p) + ".soap";
			xmlFileName  = PKG_FILE.substring(0, p) + ".xml";
			
			soapMessageInBoundProcess(soapFileName, mf);
			
			xmlFileInBoundProcess(mf);
					
		} catch (Exception e) {
			
			e.printStackTrace();
			
		}
	
	}

	/**
	 *  SOAP Message를 파싱하여 messageProcessor객체에 저장
	 */
	public void soapMessageInBoundProcess(String soapFileName, MessageFactory mf) {
		
		try {

			InputStream in          = new FileInputStream(PKG_PATH + soapFileName);
			
			// SOAP 파일을 대상으로 SOAP Messsage 생성할 경우에는 
			// MimeHeader 부분을 지정하지 않아도 파싱이 가능하다.
			SOAPMessage soapMessage = mf.createMessage(null, in);
			
			SOAPPart     soapPart     = soapMessage.getSOAPPart();
		    SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
		    SOAPHeader   soapHeader   = soapEnvelope.getHeader();
		    SOAPBody     soapBody     = soapEnvelope.getBody();
		    
		    NodeList nodeList = soapHeader.getElementsByTagName("eb:PartyId");
		    
		    if (nodeList.getLength() > 0) {
		    	
		    	messageProcessorVo.setFromPartyId(nodeList.item(0).getFirstChild().getNodeValue());
		    	messageProcessorVo.setToPartyId(nodeList.item(1).getFirstChild().getNodeValue());
		    	 
		    }
		    
		    nodeList = soapHeader.getElementsByTagName("eb:Role");
		     
		    if (nodeList.getLength() > 0) {
		    	 
		    	messageProcessorVo.setFromRole(nodeList.item(0).getFirstChild().getNodeValue());
		    	messageProcessorVo.setToRole(nodeList.item(1).getFirstChild().getNodeValue());
		    	 
		    }
		    
		    nodeList = soapHeader.getElementsByTagName("eb:CPAId");
		    
		    if (nodeList.getLength() > 0) {

		    	messageProcessorVo.setCPAId(nodeList.item(0).getFirstChild().getNodeValue());
		    	
		    }
		    
		    nodeList = soapHeader.getElementsByTagName("eb:ConversationId");
		    
		    if (nodeList.getLength() > 0) {
		    	
		    	messageProcessorVo.setConversationId(nodeList.item(0).getFirstChild().getNodeValue());
		    	
		    }
		    
		    nodeList = soapHeader.getElementsByTagName("eb:Service");
		    
		    if (nodeList.getLength() > 0) {
		    	
		    	messageProcessorVo.setService(nodeList.item(0).getFirstChild().getNodeValue());
		    	
		    }
		    
		    nodeList = soapHeader.getElementsByTagName("eb:Action");
		    
		    if (nodeList.getLength() > 0) {
		    	
		    	messageProcessorVo.setAction(nodeList.item(0).getFirstChild().getNodeValue());
		    	
		    }
		    
		    nodeList = soapHeader.getElementsByTagName("eb:MessageId");
		    
		    if (nodeList.getLength() > 0) {
		    	
		    	messageProcessorVo.setMessageId(nodeList.item(0).getFirstChild().getNodeValue());
		    	
		    }
		    
		    nodeList = soapHeader.getElementsByTagName("eb:Timestamp");
		    
		    if (nodeList.getLength() > 0) {
		    
		    	messageProcessorVo.setTimestamp(nodeList.item(0).getFirstChild().getNodeValue());
		    	
		    }
		    
		    nodeList = soapHeader.getElementsByTagName("eb:Description");
		    
		    if (nodeList.getLength() > 0) {
		    	
		    	messageProcessorVo.setDescription(nodeList.item(0).getFirstChild().getNodeValue());
		    	
		    }
		    
		    nodeList = soapBody.getElementsByTagName("eb:Description");
		    
		    if (nodeList.getLength() > 0) {
		    	
		    	messageProcessorVo.setBodyDescription(nodeList.item(0).getFirstChild().getNodeValue());
		    	
		    }
		    
			
		} catch (FileNotFoundException e) {
			
			e.printStackTrace();
			 
		} catch (IOException e) {
			
			e.printStackTrace();
			
		} catch (SOAPException e) {
			
			e.printStackTrace();
			
		}
		
	}
	
	
	/**
	 * AttachMent에 해당하는 XML을 스트링으로 반환
	 */
	public void xmlFileInBoundProcess(MessageFactory mf) {
		
		// attachment에 해당하는 마임헤더 부분을 설정한다.(설정이 맞지 않으면 에러발생)
		MimeHeaders mimeHeaders = new MimeHeaders();
		mimeHeaders.setHeader("Content-Type", "multipart/related; " +
				  			  "boundary=\"--=20090915153335453481--\"; " +
				  			  "type=\"text/xml\"; " +
				  			  "start=\"<20090915153335453481>\" ");

		mimeHeaders.setHeader("soapaction", "ebXML");
		mimeHeaders.setHeader("Content-Language", "UTF-8");
		mimeHeaders.setHeader("cache-control", "no-cache");
		mimeHeaders.setHeader("accept", "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2");
		
		try {
			
			InputStream in          = new FileInputStream(PKG_PATH + PKG_FILE);
			
			SOAPMessage pkgMessage = mf.createMessage(mimeHeaders, in);
			
			Iterator iter = pkgMessage.getAttachments();
			String   line = null;
			
			if (iter != null) {
				
				AttachmentPart    attachmentPart = null;	
				InputStream       attIn          = null; 
				InputStreamReader isr            = null;
				BufferedReader    br             = null;
				
				
				while(iter.hasNext()) {
					
					attachmentPart = (AttachmentPart)iter.next();
					attIn 		   = attachmentPart.getRawContent();
					isr 		   = new InputStreamReader(attIn);
					br 			   = new BufferedReader(isr);
					
					
					while((line = br.readLine()) != null){
						
						line += line;
						
					}
					
				}
				
				br.close();
				isr.close();
				attIn.close();
					
			}
			
			in.close();

			messageProcessorVo.setXmlContent(line);
			
		} catch (FileNotFoundException e) {
			
			e.printStackTrace();
			 
		} catch (IOException e) {
			
			e.printStackTrace();
			
		} catch (SOAPException e) {
			
			e.printStackTrace();
			
		}
		
	}
	
	/**
	 * SOAP Message 발신
	 */
	public void soapOutBoundProcess() {
		try {
			
			MessageFactory mf = MessageFactory.newInstance();
			SOAPMessage soapMessage = mf.createMessage();
			
			soapMessageOutBoundProcess(soapMessage);
			soapAttachmentOutBoundProcess(soapMessage);
			
	        soapMessage.writeTo(System.out);
		
		} catch (FileNotFoundException e) {
			
			e.printStackTrace();
			 
		} catch (IOException e) {
			
			e.printStackTrace();
			
		} catch (SOAPException e) {
			
			e.printStackTrace();
			
		}
		
	}
	


	/**
	 * SOAP Message 생성
	 * @param soapMessage
	 * @throws SOAPException 
	 */
	private SOAPMessage createSoapMessage(Cpa cpa, Payload[] payloads) throws SOAPException {
		
		MessageFactory mf = MessageFactory.newInstance();
		SOAPMessage soapMessage = mf.createMessage();
		
		try {
			
		    SOAPPart     soapPart     = soapMessage.getSOAPPart();
		    
		    // SOAP Envelope
		    SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
		    soapEnvelope.setPrefix("SOAP");
		    
		    SOAPHeader   soapHeader   = soapEnvelope.getHeader();
		    soapHeader.setPrefix("SOAP");
		    
		    SOAPBody     soapBody     = soapEnvelope.getBody();
		    soapBody.setPrefix("SOAP");
		     
		    // SOAP Envelope 속성지정 
		    soapEnvelope.removeNamespaceDeclaration("SOAP-ENV");
		    soapEnvelope.addNamespaceDeclaration("SOAP",  "http://schemas.xmlsoap.org/soap/envelope/");
		    soapEnvelope.addNamespaceDeclaration("xlink", "http://www.w3.org/1999/xlink");
		    soapEnvelope.addNamespaceDeclaration("xsi",   "http://www.w3.org/2001/XMLSchema-instance");
		    soapEnvelope.setAttribute("xsi:schemaLocation", XSI_SCHEMALOCATION);

		    // SOAP Header
	        SOAPHeader header = soapEnvelope.getHeader();
	        header.addNamespaceDeclaration(EB, NAMESPACE_EB);
	        header.setAttribute("xsi:schemaLocation", XSI_SCHEMALOCATION);
	        
	        // MessageHeader
	        SOAPElement messageHeader = header.addChildElement(new QName("http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd", "MessageHeader", EB));
	        messageHeader.setAttribute("SOAP:mustUnderstand", "1");
	        messageHeader.setAttribute("eb:id",               "MessageHeader");
	        messageHeader.setAttribute("eb:version",          "2.0");
	        
	        // eb:From
	        SOAPElement from = messageHeader.addChildElement(new QName(NAMESPACE_EB, "From", EB));
	        
	        // From eb:PartyId
	        SOAPElement fromPartyId = from.addChildElement(new QName(NAMESPACE_EB, "PartyId", EB));
	        fromPartyId.setAttribute("eb:type", "urn:kcsipt");
	        fromPartyId.addTextNode(messageProcessorVo.getFromPartyId());
	        
	        // From eb:Role
	        SOAPElement fromRole = from.addChildElement(new QName(NAMESPACE_EB, "Role", EB));
	        fromRole.addTextNode(messageProcessorVo.getFromRole());
	        
	        // To eb:From
	        SOAPElement to = messageHeader.addChildElement(new QName(NAMESPACE_EB, "To", EB));
	        
	        // To eb:PartyId
	        SOAPElement toPartyId = to.addChildElement(new QName(NAMESPACE_EB, "PartyId", EB));
	        toPartyId.setAttribute("eb:type", "urn:kcsipt");
	        toPartyId.addTextNode(messageProcessorVo.getToPartyId());
	        
	        // To eb:Role
	        SOAPElement toRole = to.addChildElement(new QName(NAMESPACE_EB, "Role", EB));
	        toRole.addTextNode(messageProcessorVo.getToRole());
	        
	        // eb:CPAId
	        SOAPElement CPAId = messageHeader.addChildElement(new QName(NAMESPACE_EB, "CPAId", EB));
	        CPAId.addTextNode(messageProcessorVo.getCPAId());
	        
	        // eb:ConversationId
	        SOAPElement conversationId = messageHeader.addChildElement(new QName(NAMESPACE_EB, "ConversationId", EB));
	        conversationId.addTextNode(messageProcessorVo.getConversationId());
	        
	        // eb:Service
	        SOAPElement service = messageHeader.addChildElement(new QName(NAMESPACE_EB, "Service", EB));
	        service.setAttribute("eb:type", "portal.customs.dtm1");
	        service.addTextNode(messageProcessorVo.getService());
	        
	        // eb:Action
	        SOAPElement action = messageHeader.addChildElement(new QName(NAMESPACE_EB, "Action", EB));
	        action.addTextNode(messageProcessorVo.getAction());
	        
	        // eb:MessageData
	        SOAPElement messageData = messageHeader.addChildElement(new QName(NAMESPACE_EB, "MessageData", EB));
	        
	        // eb:MessageId
	        SOAPElement messageId = messageData.addChildElement(new QName(NAMESPACE_EB, "MessageId", EB));
	        messageId.addTextNode(messageProcessorVo.getMessageId());
	        
	        // eb:Timestamp
	        SOAPElement timestamp = messageData.addChildElement(new QName(NAMESPACE_EB, "Timestamp", EB));
	        timestamp.addTextNode(messageProcessorVo.getTimestamp());
	        
	        // eb:Description
	        SOAPElement description = messageHeader.addChildElement(new QName(NAMESPACE_EB, "Description", EB));
	        description.setAttribute("xml:lang", "en-US");
	        description.addTextNode(messageProcessorVo.getDescription());
	        
	        // eb:SyncReply
	        SOAPElement syncReply = messageHeader.addChildElement(new QName(NAMESPACE_EB, "SyncReply", EB));
	        syncReply.setAttribute("SOAP:mustUnderstand", "1");
	        syncReply.setAttribute("eb:version",          "2.0");
	        syncReply.setAttribute("SOAP:actor",          "http://schemas.xmlsoap.org/soap/actor/next");
	        
	        // eb:AckRequested
	        SOAPElement ackRequested = messageHeader.addChildElement(new QName(NAMESPACE_EB, "AckRequested", EB));
	        ackRequested.setAttribute("SOAP:actor",          "urn:oasis:names:tc:ebxml-msg:actor:toPartyMSH");
	        ackRequested.setAttribute("SOAP:mustUnderstand", "1");
	        ackRequested.setAttribute("eb:signed",           "true");
	        ackRequested.setAttribute("eb:version",          "2.0");
	        
	        // SOAP Header
	        SOAPBody body = soapEnvelope.getBody();
	        body.addNamespaceDeclaration(EB, NAMESPACE_EB);
	        body.setAttribute("xsi:schemaLocation", XSI_SCHEMALOCATION);
	        
	        // eb:Mainfest
	        SOAPElement mainFest = body.addChildElement(new QName(NAMESPACE_EB, "Manifest", EB));
	        mainFest.setAttribute("eb:id",       "Manifest");
	        mainFest.setAttribute("eb:version",  "2.0");
	        
	        // eb:Reference
	        SOAPElement reference = mainFest.addChildElement(new QName(NAMESPACE_EB, "Reference", EB));
	        reference.setAttribute("eb:id",       "Reference-0");
	        reference.setAttribute("xlink:href",  "cid:payload-1");
	        reference.setAttribute("xlink:type",  "simple");
	        
	        // eb:Description
	        SOAPElement bodyDescription = reference.addChildElement(new QName(NAMESPACE_EB, "Description", EB));
	        bodyDescription.setAttribute("xml:lang",  "ko-kr");
	        bodyDescription.addTextNode(messageProcessorVo.getBodyDescription());
	        
	        for (Payload payload : payloads) {
	        	AttachmentPart attachment = soapMessage.createAttachmentPart();
		        attachment.setRawContent(payload.getInputStream(), payload.getType());
		        attachment.setContentId("<payload-1>");
		        soapMessage.addAttachmentPart(attachment);	
			}
		} catch (SOAPException e) {
			e.printStackTrace();
		}
		return soapMessage;
		
	}
	
	/**
	 * SOAP Message 발신 객체을 생성한다.
	 * @param soapMessage
	 * @return
	 */
	private SOAPMessage getRequestConnection(SOAPMessage soapMessage) {
		
		SOAPConnectionFactory  scf = null;
		SOAPConnection        conn = null;
		SOAPMessage requestMessage = null;
		
		try {
			
			scf            = SOAPConnectionFactory.newInstance();
			conn           = scf.createConnection();
			requestMessage = conn.call(soapMessage, new URL(REQUEST_URL));
			
		} catch (SOAPException e) {
			
			e.printStackTrace();
			
		} catch (MalformedURLException e) {
			
			e.printStackTrace();
			
		} finally {
			
			try {
				if (conn != null) conn.close();
			} catch (Exception e) {}
		}
		
		return requestMessage;
		
	}
	
	
	/**
	 * SOAP Message 수신 객체을 생성한다.
	 * @return
	 */
	private SOAPMessage getResponseConnection() {
		
		SOAPConnectionFactory  scf  = null;
		SOAPConnection        conn  = null;
		SOAPMessage responseMessage = null;      
		
		try {
			
			scf             = SOAPConnectionFactory.newInstance();
			conn            = scf.createConnection();
			responseMessage = conn.get(new URL(RESPONSE_URL));
		} catch (SOAPException e) {
			
			e.printStackTrace();
			
		} catch (MalformedURLException e) {
			
			e.printStackTrace();	
			
		} finally {
			
			try {
				if (conn != null) conn.close();
			} catch (Exception e) {}
		}
		
		return responseMessage;
		
	}
	
}
